package org.jflame.context.filemanager;

import org.jflame.commons.file.FileFilters;
import org.jflame.commons.file.FileHelper;
import org.jflame.commons.util.CollectionHelper;
import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.StringHelper;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.SystemUtils;

/**
 * 本地硬盘或NFS文件管理
 * 
 * @author charles.zhang
 */
public class LocalFileManager extends BaseFileManager<FileManagerCfg> {

    public LocalFileManager(FileManagerCfg cfg) {
        super(cfg);
        Objects.requireNonNull(cfg.getSavePath(), "savePath must not be null");
        Path path = cfg.savePath();
        if (!path.isAbsolute()) {
            throw new RuntimeException(cfg.getSavePath() + "not an absolute path");
        }
        if (!Files.exists(path)) {
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                throw new IllegalStateException("创建文件保存根目录失败" + path, e);
            }
        }
    }

    @Override
    public String save(File file, String saveDir, Map<String,String> fileMeta) throws FileAccessException {
        try {
            Path targetPath = realPath(FileHelper.mergePathToUnix(saveDir, file.getName()));
            Files.copy(file.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING);
            return relativePath(targetPath);
        } catch (IOException e) {
            throw new FileAccessException(e.getMessage());
        }
    }

    @Override
    public String save(InputStream fileStream, String filePath, Map<String,String> fileMeta, boolean replaceExisting)
            throws FileAccessException {
        Path targetPath = realPath(filePath);
        try {
            if (replaceExisting) {
                Files.copy(fileStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
            } else {
                if (Files.exists(targetPath)) {
                    String newName = createNewFileNameByExt(FileHelper.getExtension(filePath, true));
                    targetPath = targetPath.getParent()
                            .resolve(newName);
                }
                Files.copy(fileStream, targetPath);
            }
        } catch (IOException e) {
            throw new FileAccessException(e);
        } finally {
            IOHelper.closeQuietly(fileStream);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("保存上传文件到:{}", targetPath);
        }
        // 计算相对路径,统一分隔符为/
        return relativePath(targetPath);
    }

    @Override
    public byte[] readBytes(String filePath) throws FileAccessException {
        Path downFile = realPath(filePath);
        try {
            return Files.readAllBytes(downFile);
        } catch (IOException e) {
            throw new FileAccessException(e);
        }
    }

    @Override
    public File read(String filePath) throws FileAccessException {
        Path downFile = realPath(filePath);
        if (Files.exists(downFile)) {
            return downFile.toFile();
        } else {
            throw new FileAccessException("文件不存在" + downFile, FileAccessException.FILE_NOT_FOUND);
        }
    }

    @Override
    public InputStream readStream(String filePath) throws FileAccessException {
        Path downFile = realPath(filePath);
        try {
            return Files.newInputStream(downFile);
        } catch (IOException e) {
            throw new FileAccessException(e);
        }
    }

    @Override
    public void download(String filePath, Path savePath) throws FileAccessException {
        String srckey = realPath(filePath).toString();
        try {
            FileHelper.copyFile(srckey, savePath.toString(), true);
        } catch (IOException e) {
            throw new FileAccessException(e);
        }
    }

    @Override
    public int delete(String... filePaths) throws FileAccessException {
        return delete(null, filePaths);
    }

    @Override
    public int delete(String parent, String[] filePaths) throws FileAccessException {
        Path delFile = null;
        boolean result;
        int i = 0;
        FileManagerCfg config = getConfig();
        for (String filePath : filePaths) {
            if (parent != null) {
                delFile = Paths.get(config.getSavePath(), parent, filePath);
            } else {
                delFile = Paths.get(config.getSavePath(), filePath);
            }
            logger.info("删除文件{}", delFile);
            try {
                result = Files.deleteIfExists(delFile);
                if (!result) {
                    logger.warn("删除文件失败{}", delFile);
                    continue;
                }
                i++;
            } catch (IOException e) {
                logger.error("删除文件异常" + delFile, e);
            }
        }
        return i;
    }

    @Override
    public String copy(final String srcFilePath, String destFilePath) throws FileAccessException {
        String srckey = realPath(srcFilePath).toString();
        String destFile;
        if (StringHelper.isEmpty(destFilePath)) {
            int extBefore = srckey.lastIndexOf('.');
            if (extBefore < 1) {
                extBefore = srckey.length();
            }
            int i = 1;
            do {
                destFile = StringHelper.insertAt(srckey, "_copy" + (i++), extBefore);
                if (!Files.exists(Paths.get(destFile))) {
                    break;
                }
            } while (true);
        } else {
            destFile = realPath(destFilePath).toString();
            if (destFilePath.endsWith("/")) {
                destFile = destFile + "/";
            }
        }
        try {
            return FileHelper.copyFile(srckey, destFile, true);
        } catch (IOException e) {
            throw new FileAccessException(e);
        }
    }

    @Override
    public List<String> listFiles(String dir, int maxFiles, boolean isExcludeDir) throws FileAccessException {
        File parentFile = StringHelper.isEmpty(dir) || "/".equals(dir) ? getConfig().savePath()
                .toFile() : realPath(dir).toFile();
        if (!parentFile.exists()) {
            throw new FileAccessException(parentFile + " not found", FileAccessException.FILE_NOT_FOUND);
        }
        if (!parentFile.isDirectory()) {
            throw new FileAccessException(parentFile.getAbsolutePath() + "不是目录");
        }
        String[] childs;
        if (isExcludeDir) {
            childs = parentFile.list(FileFilters.JUST_FILE_NAMEFILTER);
        } else {
            childs = parentFile.list();
        }
        if (childs.length > 0 && maxFiles > 0 && maxFiles > childs.length) {
            return Arrays.stream(childs)
                    .limit(maxFiles)
                    .collect(Collectors.toList());
        }

        return CollectionHelper.newList(childs);
    }

    Path realPath(String filePath) {
        Path downFile = Paths.get(getFileId(filePath));
        downFile = getConfig().savePath()
                .resolve(filePath);

        return downFile;
    }

    private String relativePath(Path absPath) {
        String relPath = getConfig().savePath()
                .relativize(absPath)
                .toString();
        return SystemUtils.IS_OS_WINDOWS ? FileHelper.separatorsToUnix(relPath) : relPath;
    }

    @Override
    public void close() {
    }

    /*@Override
    public FileManagerMode getFileManagerMode() {
        return FileManagerMode.local;
    }*/

}
